package org.tech4j.edi.base.executor;

import com.google.common.base.Throwables;
import org.apache.commons.lang3.time.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tech4j.edi.api.RetryExecutor;
import org.tech4j.edi.api.RetryExecutorChain;
import org.tech4j.edi.api.context.RetryContext;
import org.tech4j.edi.api.context.RetryContextService;
import org.tech4j.edi.common.entity.RetryBizConfig;
import org.tech4j.edi.common.entity.RetryBizRecord;

import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @program: tech4j
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/7 16:53
 **/
public class ActuallyDoRetryExecutor implements RetryExecutor {
    private static final Logger log = LoggerFactory.getLogger(ActuallyDoRetryExecutor.class);

    private final RetryContextService retryContextService;
    public ActuallyDoRetryExecutor(RetryContextService retryContextService) {
        this.retryContextService = retryContextService;
    }

    @Override
    public void execute(RetryContext retryContext, RetryExecutorChain chain) {
        ExecutorService executorService = retryContext.getExecutorService();
        RetryBizRecord retryBizRecord = retryContext.getRetryBizRecord();
        RetryBizConfig retryBizConfig = retryContext.getRetryBizConfig();
        Map<String, AtomicInteger> retrySlowCountMap = retryContext.getRetrySlowCountMap();
        executorService.execute(() -> {
            StopWatch stopWatch = new StopWatch("real retry execute 统计耗时");
            try {
                retryContextService.retry(retryContext);
            } catch (Throwable e) {
                log.error("ActuallyDoRetryExecutor[execute] failed with throwable, e = {}, retryContext = {}"
                        , Throwables.getStackTraceAsString(e)
                        , retryContext);
            } finally {
                stopWatch.stop();
                long totalTimeMillis = stopWatch.getDuration().get(ChronoUnit.MILLIS);
                if (totalTimeMillis > retryBizConfig.getRunDuration()) {
                    int retryCount = 1;
                    AtomicInteger timeoutCount = retrySlowCountMap.putIfAbsent(retryBizRecord.getBizType(), new AtomicInteger(retryCount));
                    if (timeoutCount != null) {
                        retryCount = timeoutCount.incrementAndGet();
                    }
                    log.info("ActuallyDoRetryExecutor[execute] retry slow, retryContext = {}, cost = {}, retryCount = {}"
                            , retryContext
                            , totalTimeMillis
                            , retryCount);
                }
            }
        });
    }

    @Override
    public int getOrder() {
        return EdiExecutorsOrder.LAST.getOrder();
    }
}
